package com.security.granter;

import com.alibaba.fastjson.JSON;
import com.security.constant.AuthConstant;
import com.security.entity.User;
import com.security.entity.UserInfo;
import com.security.feign.ITestUserClient;
import com.security.feign.WeChatServiceFeign;
import com.security.service.TestUserDetails;
import com.security.util.MiniProgramUtils;
import com.security.util.TokenUtil;
import com.security.util.WebUtil;
import com.security.vo.UserOauthVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class MiniProgramTokenGranter extends AbstractTokenGranter {


	private static final String GRANT_TYPE = "mini_program";

	private static final String ACCESS_TOKEN = "access_token";

	private static final String SESSION_KEY = "session_key";

	private ITestUserClient iUserClient;

	private ClientDetailsService clientDetailsService;

	private WeChatServiceFeign wechatServiceFeign;

	protected MiniProgramTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
									  OAuth2RequestFactory requestFactory, WeChatServiceFeign wechatServiceFeign, ITestUserClient iUserClient) {
		super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
		this.iUserClient = iUserClient;
		this.clientDetailsService = clientDetailsService;
		this.wechatServiceFeign = wechatServiceFeign;
		log.info("-------------MiniProgramTokenGranter构造方法---------------------");
	}

	@Override
	public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {

		if (!this.GRANT_TYPE.equals(grantType)) {
			return null;
		}

		String clientId = tokenRequest.getClientId();
		ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
		//validateGrantType(grantType, client);

		if (logger.isDebugEnabled()) {
			logger.debug("Getting access token for: " + clientId);
		}

		return getAccessToken(client, tokenRequest);
		//return super.grant(grantType, tokenRequest);
	}

	@Override
	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

		// 请求头租户信息
		HttpServletRequest request = WebUtil.getRequest();
		Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
		String jsCode =  parameters.get("js_code");
		String encryptedData =  parameters.get("encryptedData");
		String iv = parameters.get("iv");
		// 获取租户
		String tenantCode = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
		if (StringUtils.isBlank(tenantCode)) {

			throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
		}

		//请求微信小程序登录
		//配置信息可以写在nacos的yml
		String appId = "wx175afbae652c845e";
		String appSecret = "6db66870ae15e28467d01bc996fb26ec";
		String miniProgramMsgResponse = wechatServiceFeign.jscode2session(appId,appSecret,jsCode,"authorization_code");

		Map<String,String> miniProgramMsgMap = (Map<String, String>) JSON.parse(miniProgramMsgResponse);

		String openId = miniProgramMsgMap.get("openid");

		if(StringUtils.isEmpty(miniProgramMsgResponse)|| StringUtils.isEmpty(openId)){
			throw new InvalidGrantException(TokenUtil.MINI_PROGRAM_LOGIN_ERR);
		}

		String clientTypeHeader = request.getHeader("Client-Type");

		//解密微信用户加密数据
		String wxJson = null;
		try {
			wxJson = MiniProgramUtils.wxDecrypt(encryptedData,iv,miniProgramMsgMap.get(SESSION_KEY));
		} catch (Exception e) {
			throw new InvalidGrantException(MiniProgramUtils.DECRYPT_ERR);
		}
		//更新或者插入微信用户信息

		UserOauthVO userOauthVO = iUserClient.insertMiniUserByJson(wxJson,openId);
		log.info("userOauthVO:{}",userOauthVO.toString());
		User user = userOauthVO.getUser();
		TestUserDetails userDetails = new TestUserDetails(
				user.getId(),
				user.getName(),
				user.getRealName(),
				user.getAccount(),
				AuthConstant.ENCRYPT + user.getPassword(),
				true,
				true,
				true,
				true,
				AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.join(userOauthVO.getRoles())));

		List<SimpleGrantedAuthority> authorities = userOauthVO.getRoles().stream()
				.map(authority->new SimpleGrantedAuthority(authority)).collect(Collectors.toList());

		// 组装认证数据，关闭密码校验
		AbstractAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(userDetails,
				null, authorities);
		userAuth.setDetails(parameters);
		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);

		// 返回 OAuth2Authentication
		return new OAuth2Authentication(storedOAuth2Request, userAuth);
	}
}
